#pragma once

class vector3f
{
public:
	vector3f() : x(0), y(0), z(0) {}
	vector3f(float x, float y, float z)
		: x(x), y(y), z(z)
	{}

	void normalize() {
		float v = x*x + y*y + z*z;
		if (!FLOAT_EQUALS_ZERO(v)) {
			v = 1 / sqrtf(v);
			x *= v, y *= v, z *= v;
		} else {
			x = 0, y = 0, z = 1;
		}
	}

	void rotateXZBy(float angle) {
		auto sn = sinf(angle);
		auto cs = cosf(angle);
		*this = {cs*x+sn*z, y, -sn*x+cs*z};
	}

	bool IsZero() const {
		return x == 0 && y == 0 && z == 0;
	}

	bool Has2DAngle() const {
		return x != 0 || z != 0;
	}

	float Calc2DAngle() const {
		return atan2f(x, z);
	}

	float Distance(const vector3f& other) const {
		auto dx = this->x - other.x;
		auto dy = this->y - other.y;
		auto dz = this->z - other.z;
		return sqrtf(dx*dx + dy*dy + dz*dz);
	}
	float Distance2D(const vector3f& other) const {
		auto dx = this->x - other.x;
		auto dz = this->z - other.z;
		return sqrtf(dx*dx + dz*dz);
	}
	float DistanceSq(const vector3f& other) const {
		auto dx = this->x - other.x;
		auto dy = this->y - other.y;
		auto dz = this->z - other.z;
		return dx*dx + dy*dy + dz*dz;
	}
	float Distance2DSq(const vector3f& other) const {
		auto dx = this->x - other.x;
		auto dz = this->z - other.z;
		return dx*dx + dz*dz;
	}

	float Distance(float X, float Y, float Z) const {
		auto dx = this->x - X;
		auto dy = this->y - Y;
		auto dz = this->z - Z;
		return sqrtf(dx*dx + dy*dy + dz*dz);
	}
	float Distance2D(float X, float Z) const {
		auto dx = this->x - X;
		auto dz = this->z - Z;
		return sqrtf(dx*dx + dz*dz);
	}
	float DistanceSq(float X, float Y, float Z) const {
		auto dx = this->x - X;
		auto dy = this->y - Y;
		auto dz = this->z - Z;
		return dx*dx + dy*dy + dz*dz;
	}
	float Distance2DSq(float X, float Z) const {
		auto dx = this->x - X;
		auto dz = this->z - Z;
		return dx*dx + dz*dz;
	}

	float Length() const {
		return sqrtf(x*x + y*y + z*z);
	}
	float Length2D() const {
		return sqrtf(x*x + z*z);
	}
	float LengthSq() const {
		return x*x + y*y + z*z;
	}
	float Length2DSq() const {
		return x*x + z*z;
	}

	float* Data() {
		return &x;
	}
	const float* Data() const {
		return &x;
	}

	bool operator==(const vector3f& other) const {
		return this->x == other.x && this->y == other.y && this->z == other.z;
	}
	bool operator!=(const vector3f& other) const {
		return this->x != other.x || this->y != other.y || this->z != other.z;
	}

	vector3f operator+(const vector3f& other) const {
		return {this->x + other.x, this->y + other.y, this->z + other.z};
	}
	vector3f operator-(const vector3f& other) const {
		return {this->x - other.x, this->y - other.y, this->z - other.z};
	}
	vector3f& operator+=(const vector3f& other) {
		this->x += other.x, this->y += other.y, this->z += other.z;
		return *this;
	}
	vector3f& operator-=(const vector3f& other) {
		this->x -= other.x, this->y -= other.y, this->z -= other.z;
		return *this;
	}

	vector3f operator*(float factor) const {
		return {this->x * factor, this->y * factor, this->z * factor};
	}
	vector3f operator/(float factor) const {
		return {this->x / factor, this->y / factor, this->z / factor};
	}
	vector3f& operator*=(float factor) {
		this->x *= factor, this->y *= factor, this->z *= factor;
		return *this;
	}
	vector3f& operator/=(float factor) {
		this->x /= factor, this->y /= factor, this->z /= factor;
		return *this;
	}

	float x, y, z;
};

class vector2f
{
public:
	vector2f() : x(0), y(0) {}
	vector2f(float x, float y)
		: x(x), y(y)
	{}
	vector2f(const vector3f& pos)
		: x(pos.x), y(pos.z)
	{}

	bool IsZero() const {
		return x == 0 && y == 0;
	}

	float Distance(const vector2f& other) const {
		auto dx = this->x - other.x;
		auto dy = this->y - other.y;
		return sqrtf(dx*dx + dy*dy);
	}
	float DistanceSq(const vector2f& other) const {
		auto dx = this->x - other.x;
		auto dy = this->y - other.y;
		return dx*dx + dy*dy;
	}

	float Length() const {
		return sqrtf(x*x + y*y);
	}
	float LengthSq() const {
		return x*x + y*y;
	}

	float* Data() {
		return &x;
	}
	const float* Data() const {
		return &x;
	}

	bool operator==(const vector2f& other) const {
		return this->x == other.x && this->y == other.y;
	}
	bool operator!=(const vector2f& other) const {
		return this->x != other.x || this->y != other.y;
	}

	vector2f operator+(const vector2f& other) const {
		return {this->x + other.x, this->y + other.y};
	}
	vector2f operator-(const vector2f& other) const {
		return {this->x - other.x, this->y - other.y};
	}
	vector2f& operator+=(const vector2f& other) {
		this->x += other.x, this->y += other.y;
		return *this;
	}
	vector2f& operator-=(const vector2f& other) {
		this->x -= other.x, this->y -= other.y;
		return *this;
	}

	vector2f operator*(float factor) const {
		return {this->x * factor, this->y * factor};
	}
	vector2f operator/(float factor) const {
		return {this->x / factor, this->y / factor};
	}
	vector2f& operator*=(float factor) {
		this->x *= factor, this->y *= factor;
		return *this;
	}
	vector2f& operator/=(float factor) {
		this->x /= factor, this->y /= factor;
		return *this;
	}

	float x, y;
};

class vector3f1f
{
public:
	vector3f1f() : x(0), y(0), z(0), o(0) {}
	vector3f1f(float x, float y, float z, float o)
		: x(x), y(y), z(z), o(o)
	{}
	vector3f1f(const vector3f& pos, float o)
		: x(pos.x), y(pos.y), z(pos.z), o(o)
	{}
	vector3f xyz() const { return {x,y,z}; }
	void xyz(const vector3f& pos) {
		x = pos.x, y = pos.y, z = pos.z;
	}
	float x, y, z;
	float o;
};

class vector3f3f
{
public:
	vector3f3f() : x(0), y(0), z(0), ox(0), oy(0), oz(0) {}
	vector3f3f(float x, float y, float z, float ox, float oy, float oz)
		: x(x), y(y), z(z), ox(ox), oy(oy), oz(oz)
	{}
	vector3f3f(const vector3f& pos, const vector3f& dir)
		: x(pos.x), y(pos.y), z(pos.z), ox(dir.x), oy(dir.y), oz(dir.z)
	{}
	float x, y, z;
	float ox, oy, oz;
};
